from flask import Flask
from plugin import Persistence_Scheduler_Manager, Trigger
from plugin.common import *
from job_content import *
# from db import Sqlite
from flask_admin import Admin, expose
from flask_admin.model.base import BaseModelView
from flask_admin.contrib.sqla import ModelView
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime

app = Flask(__name__)
admin = Admin(app, name='后台管理页面')
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///data.db'
# app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///data.db'
# app.config['SQLALCHEMY_DATABASE_URI'] = 'mssql+pymssql://user:password@localhost/database?charset=cp936'
db = SQLAlchemy(app)


class SchedulerView(ModelView):
    can_delete = True
    can_edit = True
    can_create = True
    can_export = True
    can_set_page_size = True
    column_display_pk = True

    # 字段别名
    column_labels = dict(
        name=u'任务名称',
        description=u'任务描述',
        task=u'任务',
        args=u'参数',
        kwargs=u'关键字参数',
        trigger_type=u'触发器类型',
        trigger_args_simple=u'触发器参数',
        is_active=u'是否启用',
        create_time=u'创建时间',
        update_time=u'更新时间',
        stack_info=u'异常信息',
    )
    # 隐藏字段
    column_exclude_list = (
        # 'create_time',
        # 'update_time',
        'trigger_args',
        'stack_info',
    )
    # 可以排序的字段
    column_sortable_list = (
        'update_time',
        'create_time',
    )
    export_types = ('xlsx', 'xls', 'csv', 'json')
    # 可以用于搜索的字段
    column_searchable_list = (
        'name',
        'description',
        'task',
        'args',
        'kwargs',
    )
    # 可以筛选的字段    
    column_filters = (  
        'name',
        'description',
        'task',
        'args',
        'kwargs',
        'is_active',
    )
    # 默认排序字段
    column_default_sort = ('create_time', True)
    # 修改/新增数据时，可以修改的字段
    form_columns = (
        'name',
        'description',
        'task',
        'args',
        'kwargs',
        'trigger_type',
        'trigger_args_simple',
        'is_active',
    )
    # 新增/修改时，给以下字段提供可选值
    form_choices = {
        'trigger_type': [('cron', 'CronTrigger'), ('date', 'DateTrigger'), ('interval', 'IntervalTrigger')],
        'task': [
            ('test_scheduler', 'test'),
            ('check_in_remind', '打卡提醒'),
        ],
        'is_active': [('1', '启动'), ('0', '停止')]
    }

    def on_model_change(self, form, model, is_created):
        try:
            # 将trigger_args_simple格式化为标准的trigger_args
            if model.trigger_type == 'cron':
                model.trigger_args = model.trigger_args_simple
            else:
                trigger_args_dict = convert_equlexp_to_dict(model.trigger_args_simple)
                trigger = getattr(Trigger, model.trigger_type)(**trigger_args_dict)
                model.trigger_args = str(trigger.__dict__)

            # 记录更新时间
            if not is_created:
                model.update_time = datetime.now()
            else:
                model.create_time = datetime.now()
                model.update_time = datetime.now()
        except Exception as e:
            print(e)
            raise e
        return super().on_model_change(form, model, is_created)

    def after_model_change(self, form, model, is_created):
        try:
            Persistence_Scheduler_Manager().refresh_presistence_job()
        except Exception as e:
            print(e)
            raise e
        return super().after_model_change(form, model, is_created)

    def after_model_delete(self, model):
        try:
            Persistence_Scheduler_Manager().refresh_presistence_job()
        except Exception as e:
            print(e)
            raise e
        return super().after_model_delete(model)

    @expose('/jobs')
    def index(self):
        return self.render('admin/scheduler/index.html')


# region 重写Flask-Admin的CSV导出功能，适应中文
def as_unicode(s):
    if isinstance(s, bytes):
        return s.decode('gb2312')
    return str(s)


def csv_encode(s):
    ''' Returns unicode string expected by Python 3's csv module '''
    return as_unicode(s)


def _export_csv(self, return_url):
    """
        Export a CSV of records as a stream.
    """
    import csv
    from flask import Response, stream_with_context
    from werkzeug.utils import secure_filename
    count, data = self._export_data()

    # https://docs.djangoproject.com/en/1.8/howto/outputting-csv/
    class Echo(object):
        """
        An object that implements just the write method of the file-like
        interface.
        """
        def write(self, value):
            """
            Write the value by returning it, instead of storing
            in a buffer.
            """
            return value

    writer = csv.writer(Echo())

    def generate():
        # Append the column titles at the beginning
        titles = [csv_encode(c[1]) for c in self._export_columns]
        yield writer.writerow(titles)

        for row in data:
            vals = [csv_encode(self.get_export_value(row, c[0])) for c in self._export_columns]
            yield writer.writerow(vals)

    filename = self.get_export_name(export_type='csv')

    disposition = 'attachment;filename=%s' % (secure_filename(filename), )

    resp = Response(stream_with_context(generate()), headers={'Content-Disposition': disposition}, mimetype='text/csv')
    resp.content_encoding = 'ansi'
    resp.charset = 'ansi'
    return resp


BaseModelView._export_csv = _export_csv
# endregion


class JOBINFOMODEL(db.Model):
    """ 定时任务的附加信息 """
    __tablename__ = 'jobinfo'
    name = db.Column(db.String(64), primary_key=True)
    description = db.Column(db.String(128))
    task = db.Column(db.String(64))
    args = db.Column(db.String(256))
    kwargs = db.Column(db.String(256))
    trigger_type = db.Column(db.String(16))
    trigger_args = db.Column(db.String(128))
    trigger_args_simple = db.Column(db.String(128))
    is_active = db.Column(db.Integer, default=1)
    create_time = db.Column(db.DateTime, default=db.func.now())
    update_time = db.Column(db.DateTime, default=db.func.now())
    stack_info = db.Column(db.String(1024))

    def __repr__(self):
        return '<JOBINFOMODEL %r>' % self.name


admin.add_view(SchedulerView(JOBINFOMODEL, db.session, name=u'任务列表', url='jobs'))

if __name__ == '__main__':
    Persistence_Scheduler_Manager()
    app.config['SECRET_KEY'] = '123456'
    app.config['SESSION_TYPE'] = 'filesystem'
    app.run(host='0.0.0.0', port=6003)
